package net.siufung.boot.executor.autoconfigure;

import com.alibaba.ttl.threadpool.TtlExecutors;
import com.google.common.eventbus.AsyncEventBus;
import lombok.AllArgsConstructor;
import net.siufung.boot.executor.ExecutorConstants;
import net.siufung.boot.executor.MdcThreadPoolExecutor;
import net.siufung.boot.executor.RejectedExecutionHandlers;
import net.siufung.boot.executor.properties.ThreadPoolExecutorProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author 陈建峰
 * @date 2022/9/26 3:59 下午
 * @since V1.0.0
 */
@Configuration
@AllArgsConstructor
@EnableConfigurationProperties({ThreadPoolExecutorProperties.class})
public class ThreadPoolExecutorAutoConfig {

    private final ThreadPoolExecutorProperties threadPoolExecutorProperties;

    /**
     * Spring 封装的线程池
     * @return ThreadPoolTaskExecutor
     */
    @Bean(name = ExecutorConstants.THREAD_POOL_TASK_EXECUTOR)
    public Executor threadPoolTaskExecutor(){
        MdcThreadPoolExecutor threadPoolTaskExecutor = new MdcThreadPoolExecutor();
        threadPoolTaskExecutor.setCorePoolSize(
                threadPoolExecutorProperties.getCorePoolSize());
        threadPoolTaskExecutor.setMaxPoolSize(
                threadPoolExecutorProperties.getMaxPoolSize());
        threadPoolTaskExecutor.setQueueCapacity(
                threadPoolExecutorProperties.getQueueCapacity());
        threadPoolTaskExecutor.setKeepAliveSeconds(
                threadPoolExecutorProperties.getKeepAliveSeconds());
        threadPoolTaskExecutor.setThreadNamePrefix(
                threadPoolExecutorProperties.getThreadNamePrefix());
        threadPoolTaskExecutor.setAllowCoreThreadTimeOut(
                threadPoolExecutorProperties.getAllowCoreThreadTimeOut());
        // 当任务完成后，长时间无待处理任务时，销毁线程池
        threadPoolTaskExecutor.setWaitForTasksToCompleteOnShutdown(
                threadPoolExecutorProperties.isWaitForTasksToCompleteOnShutdown());
        threadPoolTaskExecutor.setAwaitTerminationSeconds(
                threadPoolExecutorProperties.getAwaitTerminationSeconds());
        threadPoolTaskExecutor.setRejectedExecutionHandler(getRejectedExecutionHandler());
        return threadPoolTaskExecutor;
    }

    /**
     * 获取拒绝策略
     * @return RejectedExecutionHandler
     */
    private RejectedExecutionHandler getRejectedExecutionHandler(){
        RejectedExecutionHandler rejectedExecutionHandler = null;
        if(RejectedExecutionHandlers.callerRunsPolicy.name().equals(
                threadPoolExecutorProperties.getRejectedExecutionHandler())){
            rejectedExecutionHandler = new ThreadPoolExecutor.CallerRunsPolicy();
        }
        if(RejectedExecutionHandlers.abortPolicy.name().equals(
                threadPoolExecutorProperties.getRejectedExecutionHandler())){
            rejectedExecutionHandler = new ThreadPoolExecutor.AbortPolicy();
        }
        if(RejectedExecutionHandlers.discardOldestPolicy.name().equals(
                threadPoolExecutorProperties.getRejectedExecutionHandler())){
            rejectedExecutionHandler = new ThreadPoolExecutor.DiscardOldestPolicy();
        }
        if(RejectedExecutionHandlers.discardPolicy.name().equals(
                threadPoolExecutorProperties.getRejectedExecutionHandler())){
            rejectedExecutionHandler = new ThreadPoolExecutor.DiscardPolicy();
        }
        return rejectedExecutionHandler;
    }

    /**
     * JDK 线程池
     * @return ExecutorService
     */
    @Bean
    public ExecutorService executorService() {
        return TtlExecutors.getTtlExecutorService(new ThreadPoolExecutor(
                threadPoolExecutorProperties.getCorePoolSize(),
                threadPoolExecutorProperties.getMaxPoolSize(),
                threadPoolExecutorProperties.getKeepAliveSeconds(),
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(threadPoolExecutorProperties.getQueueCapacity()),
                Executors.defaultThreadFactory(),
                getRejectedExecutionHandler()
        ));
    }

    @Bean(name = "asyncEventBus")
    public AsyncEventBus asyncEventBus(){
        return new AsyncEventBus(threadPoolExecutorProperties.getEventbusIdentifier(), executorService());
    }
}
